/*****************************************************************************
*
*  PROJECT:     SlipStream
*               (bumpMapping - bumpMapping class)
*  LICENSE:     GNU General Public License v3 - http://www.gnu.org/licenses/gpl.html

*  DEVELOPERS:  Philip Farquharson 	<B00137077@studentmail.uws.ac.uk>
*				John Gray 			<B00145524@studentmail.uws.ac.uk>		
*				Zohaib Bhatti		<B00104280@studentmail.uws.ac.uk>
*				Euan Hislop 		<B00138636@studentmail.uws.ac.uk>
*
*****************************************************************************/

#include "bumpMapping.h"
#include "NormCubeMap.h"

GLfloat lightPos[] = {0.0f, 5.0f, 0.0f};

GLuint wallTexture;
GLuint wallNormMap;
GLuint normalisationCubeMap;

// constructor
bumpMapping::bumpMapping()
{
}

// destructor
bumpMapping::~bumpMapping()
{
}

void bumpMapping::setLightXPos(GLfloat xChange)
{
	lightPos[0] -= xChange;
}

void bumpMapping::setLightXNeg(GLfloat xChange)
{
	lightPos[0] += xChange;
}

void bumpMapping::setLightYPos(GLfloat yChange)
{
	lightPos[1] += yChange;
}

void bumpMapping::setLightYNeg(GLfloat yChange)
{
	lightPos[1] -= yChange;
}

void bumpMapping::setLightZPos(GLfloat zChange)
{
	lightPos[2] -= zChange;
}

void bumpMapping::setLightZNeg(GLfloat zChange)
{
	lightPos[2] += zChange;
}



bool bumpMapping::init()
{
	glShadeModel(GL_SMOOTH);

	loadGLTextures();

	// Setup Cg
	cgContext = cgCreateContext();							// Create A New Context For Our Cg Program(s)

	// Validate Our Context Generation Was Successful
	if (cgContext == NULL)
	{
		MessageBox(NULL, "Failed To Create Cg Context", "Error", MB_OK);
		return false;								// We Cannot Continue
	}
	cgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);				// Get The Latest GL Vertex Profile
	if (cgVertexProfile == CG_PROFILE_UNKNOWN)
	{
		MessageBox(NULL, "Invalid vertex profile type", "Error", MB_OK);
		return false;								// We Cannot Continue
	}
	cgFragProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);				// Get The Latest GL Fragment Profile
	// Validate Our Profile Determination Was Successful
	if (cgFragProfile == CG_PROFILE_UNKNOWN)
	{
		MessageBox(NULL, "Invalid fragment profile type", "Error", MB_OK);
		return false;								// We Cannot Continue
	}

	cgGLSetOptimalOptions(cgVertexProfile);						// Set The Current Profile
	cgGLSetOptimalOptions(cgFragProfile);						// Set The Current Profile

	// Load And Compile The Vertex Shader From File
	cgVertProg = cgCreateProgramFromFile(cgContext, CG_SOURCE, "vsBumpMapping.cg", cgVertexProfile, "main", 0);
	if (cgVertProg)
		cgFragProg = cgCreateProgramFromFile(cgContext, CG_SOURCE, "fsBumpMapping.cg", cgFragProfile, "main", 0);

	// Validate Success
	if (cgVertProg == NULL || cgFragProg == NULL)
	{
		// We Need To Determine What Went Wrong
		CGerror Error = cgGetError();

		// Show A Message Box Explaining What Went Wrong
		MessageBox(NULL, cgGetErrorString(Error), "Error", MB_OK);
		return false;								// We Cannot Continue
	}

	// Load The Programs
	cgGLLoadProgram(cgVertProg);
	cgGLLoadProgram(cgFragProg);
	// Get Handles To Each Of Our Parameters So That
	// We Can Change Them At Will Within Our Code
	cgModelViewProj	= cgGetNamedParameter(cgVertProg, "modelViewProj");
	if (cgModelViewProj == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'modelViewProj'", "Error", MB_OK);
		return false;	// We cannot continue
	}

	cgInvModelMatrix	= cgGetNamedParameter(cgVertProg, "invModelMatrix");
	if (cgInvModelMatrix == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'invModelMatrix'", "Error", MB_OK);
		return false;	// We cannot continue
	}
	cgLightPosW	= cgGetNamedParameter(cgVertProg, "lightPosW");
	if (cgLightPosW == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'lightPosW'", "Error", MB_OK);
		return false;	// We cannot continue
	}
	
	cgTangent	= cgGetNamedParameter(cgVertProg, "tangent");
	if (cgTangent == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'tangent'", "Error", MB_OK);
		return false;	// We cannot continue
	}

	cgTexture	= cgGetNamedParameter(cgFragProg, "texture");
	if (cgTexture == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'texture'", "Error", MB_OK);
		return false;	// We cannot continue
	}

	cgNormalMap	= cgGetNamedParameter(cgFragProg, "normalMap");
	if (cgNormalMap == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'normalMap'", "Error", MB_OK);
		return false;	// We cannot continue
	}

	cgNormaliseCubeMap	= cgGetNamedParameter(cgFragProg, "normaliseCubeMap");
	if (cgNormaliseCubeMap == NULL)
	{
		MessageBox(NULL, "Couldn't find parameter 'normaliseCubeMap'", "Error", MB_OK);
		return false;	// We cannot continue
	}	
	return true;
}


void bumpMapping::drawWall()
{	
	glPushMatrix();			
		glBegin(GL_QUADS);
				// south
				glNormal3f(-1.0f, 0.0f, 0.0f);
				glTexCoord2f(-2.0f, 0.0f);
				glVertex3f(25.0f, 0.0f, -25.0f);
				glTexCoord2f(2.0f, 0.0f);
				glVertex3f(25.0f, 0.0f, 25.0f);
				glTexCoord2f(2.0f, 2.0f);
				glVertex3f(25.0f, 45.0f, 25.0f);
				glTexCoord2f(-2.0f, 2.0f);
				glVertex3f(25.0f, 45.0f, -25.0f);				
		glEnd();		
	glPopMatrix();
}

void bumpMapping::renderBumpMap()
{
	// draw the light source for the bump mapped wall
	glPushMatrix();
		glDisable(GL_TEXTURE_2D);			
		glTranslatef(lightPos[0],lightPos[1],lightPos[2]);	
		glutSolidSphere(0.5,10,5.0);
	glPopMatrix();

	// bind the vertex and fragment shaders to the current state
	cgGLBindProgram(cgVertProg);
	cgGLBindProgram(cgFragProg);

	cgGLEnableProfile(cgVertexProfile);	// Enable the vertex shader profile
	cgGLEnableProfile(cgFragProfile); // Enable the fragment shader profile
	
	// send the modeView matrix
	cgGLSetStateMatrixParameter(cgModelViewProj, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);	

	// set the light position parameter
	cgGLSetParameter3f(cgLightPosW, lightPos[0], lightPos[1], lightPos[2]);
		
	// set the tangent parameter
	cgGLSetParameter3f(cgTangent, lightPos[0], lightPos[1], lightPos[2]);		

	// set and enable the plain wall texture 
	cgGLSetTextureParameter(cgTexture, wallTexture);
	cgGLEnableTextureParameter( cgTexture );	

	// set and enable the wall textures normal map
	cgGLSetTextureParameter(cgNormalMap, wallNormMap);
	cgGLEnableTextureParameter( cgNormalMap );

	// set and enable the normalisationCubeMap
	cgGLSetTextureParameter(cgNormaliseCubeMap, normalisationCubeMap);	
	cgGLEnableTextureParameter( cgNormaliseCubeMap );
	
	glPushMatrix();
		// send the InverseModelMatrix
		cgGLSetStateMatrixParameter(cgInvModelMatrix, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_INVERSE_TRANSPOSE);	
		// draw the wall that is to be bump mapped
		drawWall();		
	glPopMatrix();	

	glDisable(GL_TEXTURE_2D);
	// disable the shaders
	cgGLDisableProfile(cgVertexProfile);
	cgGLDisableProfile(cgFragProfile);
	
}

int bumpMapping::loadGLTextures()
{

	// load the plain wall texture
	if (!loadTexture("textures/rock.bmp", &wallTexture))
		return 0;
	// load the normal map for the wall
	if (!loadTexture("textures/rockNormal.bmp", &wallNormMap))
		return 0;
	//Create normalisation cube map
	glGenTextures(1, &normalisationCubeMap);
	glBindTexture(GL_TEXTURE_CUBE_MAP_EXT, normalisationCubeMap);
	GenerateNormalisationCubeMap();
	glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	return true;
}


bool bumpMapping::LoadBitmap(LPTSTR szFileName, GLuint &texid, GLuint target)					// Creates Texture From A Bitmap File
{
	HBITMAP hBMP;														// Handle Of The Bitmap
	BITMAP	BMP;														// Bitmap Structure
	glGenTextures(1, &texid);											// Create The Texture
	hBMP=(HBITMAP)LoadImage(GetModuleHandle(NULL), szFileName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE );

	char achError[200];
	if (!hBMP)															// Does The Bitmap Exist?
	{
		
//		MessageBox(hWnd, szFileName ,"Failed to load Bitmap", MB_OK);
//		sprintf_s(achError, 200, "Couldn't find parameter '%s'",szFileName);
		return FALSE;													// If Not Return False
	}
//	sprintf_s(achError, 200, "TEXTURE LOADED: '%s'",szFileName);

	GetObject(hBMP, sizeof(BMP), &BMP);									// Get The Object
																		// hBMP:        Handle To Graphics Object
																		// sizeof(BMP): Size Of Buffer For Object Information
																		// &BMP:        Buffer For Object Information

	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);								// Pixel Storage Mode (Word Alignment / 4 Bytes)
	
	// Typical Texture Generation Using Data From The Bitmap
	glBindTexture(target, texid);								// Bind To The Texture ID
	
	glTexImage2D(target, 0, 3, BMP.bmWidth, BMP.bmHeight, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits);
	glTexImage2D(target, 0, 3, BMP.bmWidth, BMP.bmHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, BMP.bmBits);
	glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);	// Linear Min Filter
	glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	// Linear Mag Filter
	gluBuild2DMipmaps(target, 3, BMP.bmWidth, BMP.bmHeight, GL_RGB, GL_UNSIGNED_BYTE, BMP.bmBits); 
	gluBuild2DMipmaps(target, 3, BMP.bmWidth, BMP.bmHeight, GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits); 

	DeleteObject(hBMP);													// Delete The Object

	return TRUE;														// Loading Was Successful
}

bool bumpMapping::loadTexture(char * filename, GLuint * pTexture)		// Load bitmaps and convert to textures
{
	return LoadBitmap(filename, *pTexture, GL_TEXTURE_2D);
}

bool bumpMapping::loadFace(GLenum target, char *filename)
{
	HBITMAP hBMP;														// Handle Of The Bitmap
	BITMAP	BMP;														// Bitmap Structure
	hBMP=(HBITMAP)LoadImage(GetModuleHandle(NULL), filename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE );

	char achError[200];
	if (!hBMP)															// Does The Bitmap Exist?
	{
		
//		MessageBox(hWnd, filename ,"Failed to load Face Bitmap", MB_OK);
//		sprintf_s(achError, 200, "Couldn't find parameter '%s'",filename);
		return true;													// If Not Return False
	}
//	sprintf_s(achError, 200, "TEXTURE LOADED: '%s'",filename);

	GetObject(hBMP, sizeof(BMP), &BMP);									// Get The Object
																		// hBMP:        Handle To Graphics Object
																		// sizeof(BMP): Size Of Buffer For Object Information
																		// &BMP:        Buffer For Object Information

	glPixelStorei(GL_UNPACK_ALIGNMENT, 4);								// Pixel Storage Mode (Word Alignment / 4 Bytes)
	
	glTexImage2D(target, 0, 3, BMP.bmWidth, BMP.bmHeight, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits);

	DeleteObject(hBMP);													// Delete The Object

	return true;
}
